<?php
/**
 * Created by PhpStorm.
 * User: longli
 * VX: isa1589518286
 * Date: 2020/12/30
 * Time: 17:06
 * @link http://www.lmterp.cn
 */
namespace app\common\service\product;

use app\common\library\Tools;
use app\common\model\ChannelOrders;
use app\common\model\JobFlowModule;
use app\common\model\Orders;
use app\common\model\ProductStore;
use app\common\model\Purchase;
use app\common\model\PurchaseExchange;
use app\common\model\PurchaseInfo;
use app\common\model\PurchaseReturn;
use app\common\model\PurchaseTrack;
use app\common\model\Warehouse;
use app\common\model\WarehouseAllot;
use app\common\model\WarehouseBorrow;
use app\common\model\WarehouseTerpInfo;
use app\common\model\WarehouseCheck;
use app\common\model\WarehouseStock;
use app\common\model\WarehouseStockLog;
use app\common\service\BaseService;
use app\common\service\purchase\RExService;
use app\common\status\BaseStatus;
use think\Db;
use think\exception\DbException;
use think\facade\Log;

/**
 * 库存服务类
 * Class StockService
 * @package app\common\service\product
 */
class StockService extends BaseService
{
    /**
     * 业务类型
     * @var string
     */
    protected $refType;

    /**
     * 业务主表
     * @var string
     */
    protected $refIdType;

    /**
     * 业务订单号
     * @var string
     */
    protected $refSn;

    public function __construct($refSn, $refIdType, $refType)
    {
        $this->refSn = $refSn;
        $this->refIdType = $refIdType;
        $this->refType = $refType;
    }

    /**
     * 构建库存服务类
     * @param string $refSn 业务单号
     * @param string $refIdType 业务主表
     * @param string $refType 业务类型
     * @return static
     * @date 2020/12/31
     * @author longli
     */
    public static function builder($refSn, $refIdType, $refType)
    {
        return new static($refSn, $refIdType, $refType);
    }

    /**
     * 更新库库存信息
     * @param int|WarehouseStock $stock 库存id, 或者库存信息
     * @param int $qty 数量，入库正数，出库负数
     * @param float|int $price 单价
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    protected static function updateStock($stock, $qty, $price = 0)
    {
        $stock = WarehouseStock::getObj($stock);
        if(empty($stock)) return false;
        $stock->stock += $qty;
        // 计算库存总货值
        if($price > 0) $stock->price = $price;
        $stock->total_money = ($stock->stock + $stock->lock_stock) * $stock->price;
        $stock->save();
        return true;
    }

    /**
     * 验证操作库存参数
     * @param int|Warehouse $warehouse 仓库id或者仓库
     * @param string $sku SKU
     * @param int $qty 数量，入库正数，出库负数
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    protected static function validateWarehouseStock($warehouse, $sku, $qty)
    {
        // 参数验证
        $oldVar = $warehouse;
        $warehouse = Warehouse::getObj($warehouse);
        if(empty($warehouse)) return "仓库【{$oldVar}】不存在";
        if($qty == 0) return "数量不能为0";
        if(!ProductStore::hasSku($sku)) return "没有该SKU【{$sku}】信息，请先录入";
        if(!WarehouseStock::hasSku($warehouse, $sku))
        {
            // 初始化库存信息
            WarehouseService::getInstance()->addStock(ProductStore::getStoreBySku($sku), [
                'warehouse_id' => $warehouse->warehouse_id,
            ]);
        }
        //if(!WarehouseStock::isEnough($warehouse, $sku, $qty)) return "仓库【{$warehouse->name}】SKU【{$sku}】库存不足";
        return true;
    }

    /**
     * 仓库出入库记录
     * @param int|Warehouse $warehouse 仓库id或者仓库
     * @param string $sku 商品 SKU
     * @param int $qty 数量，入库正数，出库负数
     * @param float $price 单价
     * @param array $data 业务类型
     * @return WarehouseStockLog
     * @date 2020/12/31
     * @author longli
     */
    public static function stockInfoLog($warehouse, $sku, $qty, $price, $data = [])
    {
        $warehouse = Warehouse::getObj($warehouse);
        $ws = WarehouseStock::getWarehouseBySku($warehouse, $sku);
        $store = ProductStore::getStoreBySku($sku);
        $curr = $ws->stock + $ws->lock_stock;
        $data += [
            'warehouse_id' => $warehouse->warehouse_id,
            'store_id' => $ws->store_id,
            'sku' => $sku,
            'image_url' => $store->image_url,
            'type' => $qty > 0 ? WarehouseStockLog::TYPE_IN : WarehouseStockLog::TYPE_OUT,
            'qty' => $qty,
            'user_id' => Tools::isCli() ? 0 : session("lmterp")->id,
            'price' => $price,
            'before_stock' => $curr,
            'after_stock' => $curr + $qty,
        ];
        if(in_array($data['type'], [WarehouseStockLog::TYPE_LOCK, WarehouseStockLog::TYPE_UNLOCK])) $data['after_stock'] = $curr;
        $iData = WarehouseStockLog::getFilterField($data, '');
        return WarehouseStockLog::create($iData);
    }

    /**
     * 采购入库上架
     * @param int|Purchase $purchase 采购id, 或者采购单
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    public static function purchaseStock($purchase)
    {
        $purchase = Purchase::getObj($purchase);
        if(empty($purchase)) return false;
        if(!in_array($purchase->getData('order_status'), [
            Purchase::ORDER_STATUS_RECV, Purchase::ORDER_STATUS_CHE_INFO,
            Purchase::ORDER_STATUS_RET, Purchase::ORDER_STATUS_EX,
            Purchase::ORDER_STATUS_IN, Purchase::ORDER_STATUS_WAIT_UP,
            Purchase::ORDER_STATUS_IN_PART,
        ])) return "采购单【{$purchase->purchase_sn}】状态为【{$purchase->order_status}】不能执行入库上架";
        $warehouse = Warehouse::get($purchase->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($purchase->info as $info)
            {
                if($info->store_qty < 1) continue;
                // 验证参数信息
                if(($valiMsg = self::validateWarehouseStock($warehouse, $info->sku, $info->store_qty)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                // 添加入库记录
                self::stockInfoLog($warehouse, $info->sku, $info->store_qty, $info->price, [
                    'ref_type' => BaseStatus::REF_TYPE_PURCHASE,
                    'ref_id_type' => Purchase::getTable(),
                    'ref_sn' => $purchase->purchase_sn,
                    'user_id' => $purchase->buyer_user_id,
                    'remark' => '采购入库',
                ]);
                // 获取库存信息
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
                // 入库数量
                self::updateStock($stock, $info->store_qty, $info->price);
                $inStock = $info->store_qty + $info->return_qty;
                $info->stock_qty += $info->store_qty; // 已入库数量
                $info->store_qty = 0; // 到货数量置 0
                $info->n_store_qty = $info->getNotStoreQty();
                $info->store_status = $info->n_store_qty == 0
                    ? PurchaseInfo::STORE_SUCC
                    : PurchaseInfo::STORE_PART_UP;
                $info->save();
                // 释放在途库存
                self::purchaseReleaseStock($stock, $inStock);
            }
            // 更新采购单入库状态
            $purchase->order_status = $purchase->isInStoreAll()
                ? Purchase::ORDER_STATUS_IN_SUCC
                : Purchase::ORDER_STATUS_IN_PART;
            $purchase->save();
            Db::commit(); // 提交事物
            return true;
        }catch (DbException $exception)
        {
            Log::info(sprintf("采购单【%s】入库失败，错误信息【%s】", $purchase->purchase_sn, $exception->getMessage()));
            Db::rollback();
        }
        return false;
    }

    /**
     * 通过采购单生成在途库存
     * @param int|Purchase $purchase 采购id, 或者采购单
     * @return bool|string
     * @date 2021/01/06
     * @author longli
     */
    public static function purchaseInfoIngStock($purchase)
    {
        $purchase = Purchase::getObj($purchase);
        if(empty($purchase)) return "采购单不存在";
        if(!in_array($purchase->getData('order_status'), [Purchase::ORDER_STATUS_SHIPPING]))
            return "采购单【{$purchase->purchase_sn}】状态为【{$purchase->order_status}】不能生成在途库存";
        $warehouse = Warehouse::getObj($purchase->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($purchase->info as $info)
            {
                $qtyIng = $info->qty - $info->stock_qty - $info->store_qty - $info->return_qty;
                if($qtyIng < 1) continue;
                if(($valiMsg = self::validateWarehouseStock($warehouse, $info->sku, $qtyIng)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
                $stock->purchase_stock += $qtyIng;
                $stock->save();
            }
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("采购单【%s】在途库存更新失败，错误信息【%s】", $purchase->purchase_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 通过追踪号生成采购在途库存
     * @param int|PurchaseTrack $track 采购跟踪id, 或者采购跟踪
     * @return bool|string
     * @date 2020/12/31
     * @author longli
     */
    public static function purchaseIngStock($track)
    {
        $track = PurchaseTrack::getObj($track);
        if(empty($track)) return "采购追踪单号不存在";
        if($track->is_recv == PurchaseTrack::IS_YES) return "追踪号已收货";
        $purchase = $track->purchase;
        $warehouse = Warehouse::getObj($purchase->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($track->info as $info)
            {
                // 验证参数信息
                if(($valiMsg = self::validateWarehouseStock($warehouse, $info->sku, $info->qty)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
                $stock->purchase_stock += $info->qty;
                $stock->save();
            }
            if($purchase->getData('order_status') < Purchase::ORDER_STATUS_SHIPPING)
            {
                $purchase->order_status = Purchase::ORDER_STATUS_SHIPPING;
                $purchase->save();
            }
            Db::commit();
            return  true;
        }catch (DbException $exception)
        {
            Log::info(sprintf("采购单【%s】在途库存更新失败，错误信息【%s】", $purchase->purchase_sn, $exception->getMessage()));
            Db::rollback();
        }
        return false;
    }

    /**
     * 采购退货占用库存
     * @param int|PurchaseReturn $ren 退货单id, 或者退货单
     * @return bool|string
     * @date 2021/01/05
     * @author longli
     */
    public static function purchaseReturnLock($ren)
    {
        $ren = PurchaseReturn::getObj($ren);
        if(empty($ren)) return "退货单不存在";
        if(!in_array($ren->purchase->getData('order_status'), [Purchase::ORDER_STATUS_IN_PART, Purchase::ORDER_STATUS_IN_SUCC]))
            return "未上架不需要占用库存";
        if(!JobFlowModule::isCheckFinish($ren))
            return "退货单【{$ren->return_sn}】未审批不能占用库存";
        if($ren->getData('status') != PurchaseReturn::STATUS_RETURN_WAIT)
            return "退货单【{$ren->return_sn}】状态为【{$ren->status}】不能占用库存";
        if(RExService::getInstance()->isReNStore($ren)) return true;
        $idType = PurchaseReturn::getTable();
        $stockService = self::builder($ren->return_sn, $idType, BaseStatus::REF_TYPE_PURCHASE_RETURN);
        $warehouse = Warehouse::get($ren->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($ren->info as $info)
            {
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
                if(($lockMsg = $stockService->lockStock($stock, $info->qty)) !== true)
                {
                    Db::rollback();
                    return $lockMsg;
                }
            }
            $ren->status = PurchaseReturn::STATUS_RETURN_LOCK_STOCK;
            $ren->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Db::rollback();
            Log::info(sprintf("退货单【%s】占用库存失败，错误信息【%s】", $ren->return_sn, $exception->getMessage()));
            return false;
        }
        return true;
    }

    /**
     * 释放采购退货占用库存
     * @param $ren
     * @return bool
     * @date 2021/07/02
     * @author longli
     */
    public static function purchaseReturnUnlock($ren)
    {
        // @todo
    }

    /**
     * 采购退货出库
     * @param int|PurchaseReturn $ren 退货单id, 或者退货单
     * @return bool|string
     * @date 2021/01/05
     * @author longli
     */
    public static function purchaseReturnOut($ren)
    {
        $ren = PurchaseReturn::getObj($ren);
        if(empty($ren)) return "退货单不存在";
        if($ren->getData('status') != PurchaseReturn::STATUS_RETURN_WAIT_OUT)
            return "退货单【{$ren->return_sn}】状态为【{$ren->status}】，不能出库";
        $idType = PurchaseReturn::getTable();
        $stockService = self::builder($ren->return_sn, $idType, BaseStatus::REF_TYPE_PURCHASE_RETURN);
        $warehouse = Warehouse::getObj($ren->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($ren->info as $info)
            {
                $qty = abs($info->qty);
                $purInfo = PurchaseInfo::getBySku($info->sku, $ren->purchase_id);
                $stockOut = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
                // 释放占用库存
                if(($valiMsg = $stockService->unlockStock($stockOut, $info->qty, false)) === true)
                {
                    // 处理出库
                    self::stockInfoLog($warehouse, $info->sku, -$qty, $info->price, [
                        'ref_type' => BaseStatus::REF_TYPE_PURCHASE_RETURN,
                        'ref_id_type' => $idType,
                        'ref_sn' => $ren->return_sn,
                        'user_id' => $ren->create_by,
                        'remark' => '采购退货出库',
                    ]);
                    // 扣减库存
                    self::updateStock($stockOut, -$qty);
                    // 扣减采购单已入库数量
                    $purInfo->stock_qty -= $qty;
                }
                else if($purInfo->stock_qty > $qty && $purInfo->n_store_qty < $qty)
                {
                    Db::rollback();
                    return "SKU【{$info->sku}】未占用库存，请先占用库存";
                }
                // 释放在途库存
                if($ren->purchase->getData('order_status') < Purchase::ORDER_STATUS_IN_SUCC)
                {
                    self::purchaseReleaseStock(WarehouseStock::getWarehouseBySku($warehouse, $info->sku), $qty);
                }
                $purInfo->return_qty += $qty;
                $purInfo->n_store_qty = $purInfo->getNotStoreQty();
                $purInfo->store_status = $purInfo->n_store_qty > 0
                    ? PurchaseInfo::STORE_RETURN
                    : PurchaseInfo::STORE_SUCC;
                $purInfo->save();
            }
            $ren->status = PurchaseReturn::STATUS_RETURN_OUT;
            $ren->save();
            if($ren->purchase->isInStoreAll())
            {
                $ren->purchase->order_status = Purchase::ORDER_STATUS_IN_SUCC;
                $ren->purchase->save();
            }
            Db::commit();
        }catch (DbException $exception)
        {
            Db::rollback();
            Log::info(sprintf("退货单【%s】出库失败，错误信息【%s】", $ren->return_sn, $exception->getMessage()));
            return false;
        }
        return true;
    }

    /**
     * 采购换货占用库存
     * @param int|PurchaseExchange $exchange 换货单id, 或者换货单
     * @return bool|string
     * @date 2021/01/05
     * @author longli
     */
    public static function purchaseExchangeLock($exchange)
    {
        $exchange = PurchaseExchange::getObj($exchange);
        if(empty($exchange)) return "换货单不存在";
        if(!in_array($exchange->purchase->getData('order_status'), [Purchase::ORDER_STATUS_IN_PART, Purchase::ORDER_STATUS_IN_SUCC]))
            return "未上架不需要占用库存";
        if(!JobFlowModule::isCheckFinish($exchange))
            return "换货单【{$exchange->exchange_sn}】未审批不能占用库存";
        if($exchange->getData('status') != PurchaseExchange::STATUS_EXCHANGE_WAIT)
            return "换货单【{$exchange->exchange_sn}】状态为【{$exchange->status}】不能占用库存";
        if(RExService::getInstance()->isReNStore($exchange)) return true;
        $idType = PurchaseExchange::getTable();
        $stockService = self::builder($exchange->exchange_sn, $idType, BaseStatus::REF_TYPE_PURCHASE_EXCHANGE);
        $warehouse = Warehouse::get($exchange->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($exchange->info as $info)
            {
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
                if(($lockMsg = $stockService->lockStock($stock, $info->qty)) !== true)
                {
                    Db::rollback();
                    return $lockMsg;
                }
            }
            $exchange->status = PurchaseExchange::STATUS_EXCHANGE_LOCK_STOCK;
            $exchange->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Db::rollback();
            Log::info(sprintf("换货单【%s】占用库存失败，错误信息【%s】", $exchange->exchange_sn, $exception->getMessage()));
            return false;
        }
        return true;
    }

    /**
     * 释放采购换货占用库存
     * @param $exchange
     * @return bool
     * @date 2021/07/02
     * @author longli
     */
    public static function purchaseExchangeUnlock($exchange)
    {
        // @todo
    }

    /**
     * 采购换货出库
     * @param int|PurchaseExchange $exchange 换货单id, 或者换货单
     * @return bool|string
     * @date 2021/01/05
     * @author longli
     */
    public static function purchaseExchangeOut($exchange)
    {
        $exchange = PurchaseExchange::getObj($exchange);
        if(empty($exchange)) return "换货单不存在";
        if($exchange->getData('status') != PurchaseExchange::STATUS_EXCHANGE_WAIT_OUT)
            return "换货单【{$exchange->exchange_sn}】状态为【{$exchange->status}】，不能出库";
        $idType = PurchaseExchange::getTable();
        $stockService = self::builder($exchange->exchange_sn, $idType, BaseStatus::REF_TYPE_PURCHASE_EXCHANGE);
        $warehouse = Warehouse::getObj($exchange->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($exchange->info as $info)
            {
                $qty = abs($info->qty);
                $purInfo = PurchaseInfo::getBySku($info->sku, $exchange->purchase_id);
                $stockOut = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
                // 释放占用库存
                if(($valiMsg = $stockService->unlockStock($stockOut, $qty, false)) === true)
                {
                    // 处理出库
                    self::stockInfoLog($warehouse, $info->sku, -$qty, $info->price, [
                        'ref_type' => BaseStatus::REF_TYPE_PURCHASE_EXCHANGE,
                        'ref_id_type' => $idType,
                        'ref_sn' => $exchange->exchange_sn,
                        'user_id' => $exchange->create_by,
                        'remark' => '采购换货出库',
                    ]);
                    // 扣减库存
                    self::updateStock($stockOut, -$qty);
                    // 扣减采购单已入库数量
                    $purInfo->stock_qty -= $qty;
                }
                else if($purInfo->stock_qty > $qty && $purInfo->n_store_qty < $qty)
                {
                    Db::rollback();
                    return "SKU【{$info->sku}】未占用库存，请先占用库存";
                }
                // 添加未到货数量
                $purInfo->n_store_qty = $purInfo->getNotStoreQty();
                $purInfo->store_status = PurchaseInfo::STORE_EXCHANGE;
                $purInfo->save();
            }
            $exchange->status = PurchaseExchange::STATUS_EXCHANGE_OUT;
            $exchange->save();
            // 更新采购单入库状态
            $purchase = $exchange->purchase;
            $purchase->order_status = $purchase->isInStoreAll()
                ? Purchase::ORDER_STATUS_IN_SUCC
                : Purchase::ORDER_STATUS_IN_PART;
            $purchase->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Db::rollback();
            Log::info(sprintf("换货单【%s】出库失败，错误信息【%s】", $exchange->exchange_sn, $exception->getMessage()));
            return false;
        }
        return true;
    }

    /**
     * 释放采购在途库存
     * @param int|WarehouseStock $ws 仓库
     * @param int $qty 释放数量
     * @return bool|string
     * @date 2020/12/31
     * @author longli
     */
    public static function purchaseReleaseStock($ws, $qty)
    {
        $ws = WarehouseStock::getObj($ws);
        $qty = abs($qty);
        if(empty($ws) || $qty < 1) return false;
        $ws->purchase_stock = $ws->purchase_stock > $qty ? $ws->purchase_stock - $qty : 0;
        $ws->save();
        return true;
    }

    /**
     * 生成调拨单
     * @param int|Warehouse $warehouseOut 调出仓库
     * @param int|Warehouse $warehouseIn 调入仓库
     * @param string $allotDate 调拨日期
     * @param array $data 调拨商品 SKU
     * @example
     * [
     *     [
     *         "sku" => 'A001',
     *         "qty" => 5,
     *    ],
     *    ....
     * ]
     * @param string $remark 备注
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    public static function allotOrder($warehouseOut, $warehouseIn, $allotDate, array $data, $remark = '')
    {
        if(empty($data)) return "调拨商品不能为空";
        $out = Warehouse::getObj($warehouseOut);
        $in = Warehouse::getObj($warehouseIn);
        if(empty($out) || empty($in)) return "调拨仓库不存在";
        if($out->warehouse_id == $in->warehouse_id) return "同一个仓库无需调拨";
        $refSn = WarehouseService::getInstance()->generateAllotSn();
        $userId = session("lmterp")->id;
        try
        {
            Db::startTrans();
            $allot = WarehouseAllot::create([
                'allot_sn' => $refSn,
                'out_id' => $out->warehouse_id,
                'in_id' => $in->warehouse_id,
                'user_id' => $userId,
                'allot_date' => $allotDate,
                'remark' => trim($remark),
            ]);
            $data = Tools::trim($data);
            foreach($data as $item)
            {
                if(!WarehouseStock::isEnough($out, $item['sku'], $item['qty']))
                {
                    Db::rollback();
                    return sprintf("仓库【%s】SKU【%s】库存不足", $out->name, $item['sku']);
                }
                $stock = WarehouseStock::getWarehouseBySku($out, $item['sku']);
                WarehouseTerpInfo::create([
                    'id_type' => WarehouseAllot::getTable(),
                    'ref_id' => $allot->allot_id,
                    'warehouse_id' => $stock->warehouse_id,
                    'store_id' => $stock->store_id,
                    'price' => $stock->price,
                    'sku' => $item['sku'],
                    'qty' => $item['qty'],
                    'image_url' => $stock->image_url,
                    'remark' => !empty($item['remark']) ? $item['remark'] : '',
                ]);
            }
            Db::commit();
        }catch(DbException $exception)
        {
            Log::info(sprintf("仓库【%s】调拨到仓库【%s】失败，错误信息【%s】", $out->name, $in->name, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 调拨单占用库存
     * @param int|string 调拨id,或者调拨单号
     * @return bool|string
     * @date 2021/01/08
     * @author longli
     */
    public static function allotLock($allotSn)
    {
        $allot = WarehouseAllot::getBySn($allotSn);
        if(empty($allot)) return "调拨单不存在";
        if(!JobFlowModule::isCheckFinish($allot)) return  "调拨单【{$allot->allot_sn}】未审批通过，不能占用库存";
        $stockService = StockService::builder($allot->allot_sn, WarehouseAllot::getTable(), BaseStatus::REF_TYPE_WMS_ALLOT);
        $warehouse = Warehouse::get($allot->out_id);
        Db::startTrans();
        foreach($allot->info as $info)
        {
            $stock = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
            if(($lockMsg = $stockService->lockStock($stock, $info->qty)) !== true)
            {
                Db::rollback();
                return $lockMsg;
            }
        }
        Db::commit();
        return true;
    }

    /**
     * 调拨出库
     * @param int|string 调拨id,或者调拨单号
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    public static function allotOut($allotSn)
    {
        $allot = WarehouseAllot::getBySn($allotSn);
        if(empty($allot)) return "调拨单【{$allotSn}】不存在";
        if(!JobFlowModule::isCheckFinish($allot)) return  "调拨单【{$allot->allot_sn}】未审批通过，不能出库";
        $idType = WarehouseAllot::getTable();
        $stockService = StockService::builder($allot->allot_sn, $idType, BaseStatus::REF_TYPE_WMS_ALLOT);
        $wOut = Warehouse::getObj($allot->out_id);
        $wIn = Warehouse::getObj($allot->in_id);
        try
        {
            Db::startTrans();
            foreach($allot->info as $info)
            {
                $qty = abs($info->qty);
                $stockOut = WarehouseStock::getWarehouseBySku($wOut, $info->sku);
                // 释放占用库存
                if(($valiMsg = $stockService->unlockStock($stockOut, $qty, false)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                // 处理出库
                self::stockInfoLog($wOut, $info->sku, -$qty, $info->price, [
                    'ref_type' => BaseStatus::REF_TYPE_WMS_ALLOT,
                    'ref_id_type' => $idType,
                    'ref_sn' => $allot->allot_sn,
                    'user_id' => $allot->user_id,
                    'remark' => '调拨出库',
                ]);
                self::updateStock($stockOut, -$qty);
                // 处理在途库存
                if(($valiMsg = self::validateWarehouseStock($wIn, $info->sku, $qty)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                $stock = WarehouseStock::getWarehouseBySku($wIn, $info->sku);
                $stock->purchase_stock += $qty;
                $stock->save();
            }
            $allot->status = WarehouseAllot::ALLOT_STATUS_ING;
            $allot->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("调拨单出库失败【%s】，错误信息【%s】", $allot->allot_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 调拨入库
     * @param int|string 调拨id,或者调拨单号
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    public static function allotIn($allotSn)
    {
        $allot = WarehouseAllot::getBySn($allotSn);
        if(empty($allot)) return "调拨单【{$allotSn}】不存在";
        if(!in_array($allot->getData('status'), [WarehouseAllot::ALLOT_STATUS_WAIT_UP]))
            return  "调拨单【{$allot->allot_sn}】状态为【{$allot->status}】，不能执行入库";
        $idType = WarehouseAllot::getTable();
        $wIn = Warehouse::getObj($allot->in_id);
        try
        {
            Db::startTrans();
            foreach($allot->info as $info)
            {
                $qty = abs($info->qty);
                // 处理入库
                if(($valiMsg = self::validateWarehouseStock($wIn, $info->sku, $qty)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                self::stockInfoLog($wIn, $info->sku, $qty, $info->price, [
                    'ref_type' => BaseStatus::REF_TYPE_WMS_ALLOT,
                    'ref_id_type' => $idType,
                    'ref_sn' => $allot->allot_sn,
                    'user_id' => $allot->user_id,
                    'remark' => '调拨入库',
                ]);
                $stockIn = WarehouseStock::getWarehouseBySku($wIn, $info->sku);
                $info->in_qty = $qty;
                $info->save();
                self::updateStock($stockIn, $qty);
                // 释放在途库存
                self::purchaseReleaseStock($stockIn, $qty);
            }
            $allot->status = WarehouseAllot::ALLOT_STATUS_SUCC;
            $allot->finish_date = Tools::now();
            $allot->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("调拨单【%s】入库失败，错误信息【%s】", $allot->allot_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * FBA 订单出库
     * @param int|Orders $order 订单id或者订单
     * @return bool|string
     * @date 2021/03/09
     * @author longli
     */
    public static function orderFBA($order)
    {
        // @todo FBA 订单
    }

    /**
     * 虚拟仓库订单
     * @return bool|string
     * @date 2021/03/09
     * @author longli
     */
    public static function orderVirtual()
    {
        // @todo
    }

    /**
     * 订单退货入库
     * @param int|Orders $order 订单id或者订单
     * @param int|Warehouse $wid 退货仓库, 默认为发货仓库
     * @return bool|string
     * @date 2021/01/01
     * @author longli
     */
    public static function orderReturn($order, $wid = 0)
    {
        $order = Orders::getObj($order);
        if(!in_array($order->getData('send_status'), [Orders::SEND_RETURN_PART, Orders::SEND_RETURN_ALL]))
            return "订单未退货不能执行入库操作";
        $wid = ($wid instanceof Warehouse) || $wid > 0 ? $wid : $order->warehouse_id;
        $warehouse = Warehouse::getObj($wid);
        if(empty($warehouse)) return "退货仓库【{$wid}】不存在";
        try
        {
            Db::startTrans();
            foreach($order->detail as $detail)
            {
                if($detail->is_recv == Orders::IS_YES || $detail->return_qty < 1) continue;
                if($detail->qty < $detail->return_qty)
                {
                    Db::rollback();
                    return "SKU【{$detail->sku}】退货数量不能大于购买数量";
                }
                if(($valiMsg = self::validateWarehouseStock($warehouse, $detail->sku, $detail->return_qty)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                // 单价为订单原币种
                self::stockInfoLog($warehouse, $detail->sku, $detail->return_qty, $detail->price, [
                    'ref_type' => BaseStatus::REF_TYPE_ORDER_RETURN,
                    'ref_id_type' => Orders::getTable(),
                    'ref_sn' => $order->order_sn,
                    'user_id' => 0,
                    'remark' => '订单退货入库',
                ]);
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $detail->sku);
                // 更新库存信息
                self::updateStock($stock, $detail->return_qty);
                // 更新退货状态
                $detail->is_recv = Orders::IS_YES;
                $detail->save();
            }
            Db::commit();
        }catch(DbException $exception)
        {
            Log::info(sprintf("订单【%s】退货入库失败，错误信息【%s】", $order->order_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }


    /**
     * 订单出库
     * @param int|Orders $order 订单id或者订单
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    public static function orderStock($order)
    {
        $order = Orders::getObj($order);
        if(!in_array($order->getData('order_status'), [Orders::ORDER_PACKAGE, Orders::ORDER_WAIT_OUT]))
            return "订单【{$order->order_sn}】状态为【{$order->order_status}】不能出库";
        $idType = Orders::getTable();
        $stockService = self::builder($order->order_sn, $idType, BaseStatus::REF_TYPE_ORDER_OUT);
        $warehouse = Warehouse::getObj($order->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($order->detail as $detail)
            {
                $stockOut = WarehouseStock::getWarehouseBySku($warehouse, $detail->sku);
                // 释放占用库存
                if(($msg = $stockService->unlockStock($stockOut, $detail->qty, false)) !== true)
                {
                    Db::rollback();
                    return $msg;
                }
                // 单价为订单原币种
                self::stockInfoLog($order->warehouse_id, $detail->sku, -$detail->qty, $detail->price, [
                    'ref_type' => BaseStatus::REF_TYPE_ORDER_OUT,
                    'ref_id_type' => $idType,
                    'ref_sn' => $order->order_sn,
                    'user_id' => 0,
                    'remark' => '订单出库',
                ]);
                self::updateStock($stockOut, -$detail->qty);
            }
            $order->send_status = Orders::SEND_ALL;
            $order->order_status = Orders::ORDER_SEND;
            $order->shipping_time = Tools::now();
            $order->save();
            if($chOrder = ChannelOrders::get(['order_id' => $order->order_id, 'track_num' => $order->track_num]))
            {
                // 更新物流订单状态
                $chOrder->track_status = ChannelOrders::TRACK_STATUS_OUT;
                $chOrder->save();
            }
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("订单【%s】出库失败，错误信息【%s】", $order->order_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 订单占用库存
     * @param int|Orders $order 订单id或者订单
     * @return bool|string
     * @date 2021/01/01
     * @author longli
     */
    public static function orderLockStock($order)
    {
        $order = Orders::getObj($order);
        if(empty($order->warehouse_id)) return "订单【{$order->order_sn}】未分仓";
        if($order->getData('order_status') == Orders::ORDER_LOCK) return "订单【{$order->order_sn}】已占用库存";
        $idType = Orders::getTable();
        $stockService = self::builder($order->order_sn, $idType, BaseStatus::REF_TYPE_ORDER_LOCK);
        $warehouse = Warehouse::getObj($order->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($order->detail as $detail)
            {
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $detail->sku);
                if(($msg = $stockService->lockStock($stock, $detail->qty, 0, true)) !== true)
                {
                    Db::rollback();
                    return $msg;
                }
            }
            $order->order_status = !empty($order->track_num)
                ? Orders::ORDER_DECLARE_SUCC
                : Orders::ORDER_LOCK;
            $order->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("订单【%s】占用库存失败，错误信息【%s】", $order->order_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 订单释放占用库存
     * @param int|Orders $order 订单id或者订单
     * @return bool|string
     * @date 2021/01/13
     * @author longli
     */
    public static function orderUnlockStock($order)
    {
        $order = Orders::getObj($order);
        if(empty($order)) return "订单不存在";
        if($order->getData('order_status') < Orders::ORDER_LOCK) return "订单【{$order->order_sn}】未占用库存";
        $idType = Orders::getTable();
        $stockService = self::builder($order->order_sn, $idType, BaseStatus::REF_TYPE_ORDER_UNLOCK);
        $warehouse = Warehouse::getObj($order->warehouse_id);
        try
        {
            Db::startTrans();
            foreach($order->detail as $detail)
            {
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $detail->sku);
                if(($lockMsg = $stockService->unlockStock($stock, $detail->qty)) !== true)
                {
                    Db::rollback();
                    return $lockMsg;
                }
            }
            if($order->getData('order_status') <= Orders::ORDER_LOCK)
            {
                $order->order_status = Orders::ORDER_UNLOCK;
                $order->save();
            }
            Db::commit();
        }catch (DbException $exception){}
        return true;
    }

    /**
     * 占用库存
     * @param int|WarehouseStock $stock 库存id，或者库存
     * @param int $qty 锁定数量
     * @param int $userId 占用库存用户id
     * @param bool $force 是否强制占用库存，默认否
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    public function lockStock($stock, $qty, $userId = 0, $force = false)
    {
        $stock = WarehouseStock::getObj($stock);
        if(empty($stock)) return "库存信息不存在";
        $qty = abs($qty);
        $warehouse = Warehouse::getObj($stock->warehouse_id);
        if(($valiMsg = self::validateWarehouseStock($warehouse, $stock->sku, $qty)) !== true) return $valiMsg;
        if(!WarehouseStock::isEnough($warehouse, $stock->sku, $qty)) return sprintf("仓库【%s】SKU【%s】库存不足", $warehouse->name, $stock->sku);
        if(!$force && WarehouseStockLog::hasBySn($this->refSn, $warehouse->warehouse_id, $stock->sku, -$qty, $this->refIdType)) return true;
        try
        {
            Db::startTrans();
            self::stockInfoLog($warehouse, $stock->sku, -$qty, $stock->price, [
                'ref_type' => $this->refType,
                'ref_id_type' => $this->refIdType,
                'ref_sn' => $this->refSn,
                'type' => WarehouseStockLog::TYPE_LOCK,
                'user_id' => $userId,
                'remark' => '占用库存',
            ]);
            $stock->stock -= $qty;
            $stock->lock_stock += $qty;
            $stock->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("仓库【%s】占用 SKU【%s】占用库存失败，数量【%d】，错误信息【%s】", $warehouse->name, $stock->sku, $qty, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 释放占用库存
     * @param int|WarehouseStock $stock 库存id，或者库存
     * @param int $qty 释放数量
     * @param bool $record 是否记录释放库存日志
     * @return bool|string
     * @date 2020/12/30
     * @author longli
     */
    public function unlockStock($stock, $qty, $record = true)
    {
        $stock = WarehouseStock::getObj($stock);
        if(empty($stock)) return "库存信息不存在";
        $qty = abs($qty);
        $warehouse = Warehouse::getObj($stock->warehouse_id);
        if(($valiMsg = self::validateWarehouseStock($warehouse, $stock->sku, $qty)) !== true) return $valiMsg;
        if(!WarehouseStockLog::hasBySn($this->refSn, $warehouse->warehouse_id, $stock->sku, -$qty, $this->refIdType))
            return "业务单号【{$this->refSn}】未占用库存";
        try
        {
            Db::startTrans();
            if($record)
            {
                self::stockInfoLog($warehouse, $stock->sku, $qty, $stock->price, [
                    'ref_type' => $this->refType,
                    'ref_id_type' => $this->refIdType,
                    'ref_sn' => $this->refSn,
                    'type' => WarehouseStockLog::TYPE_UNLOCK,
                    'user_id' => 0,
                    'remark' => '释放占用库存',
                ]);
            }
            $stock->stock += $qty;
            $stock->lock_stock -= $qty;
            $stock->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("仓库【%s】释放 SKU【%s】库存失败，数量【%d】，错误信息【%s】", $warehouse->name, $stock->sku, $qty, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 创建借用单
     * @param int|Warehouse $warehouse 仓库id，或者仓库
     * @param array $data 借用商品 SKU
     * @param string $remark 备注
     * @return bool|string
     * @date 2020/01/04
     * @example
     * [
     *     [
     *         "sku" => 'A001',
     *         "qty" => 5,
     *    ],
     *    ....
     * ]
     * @author longli
     */
    public static function borrowOrder($warehouse, array $data, $remark = '')
    {
        if(empty($data)) return "借用商品不能为空";
        $warehouse = Warehouse::getObj($warehouse);
        if(empty($warehouse)) return "借用仓库不存在";
        $refSn = WarehouseService::getInstance()->generateBorrowSn();
        $userId = session("lmterp")->id;
        try
        {
            Db::startTrans();
            $borrow = WarehouseBorrow::create([
                'borrow_sn' => $refSn,
                'out_id' => $warehouse->warehouse_id,
                'user_id' => $userId,
                'remark' => trim($remark),
            ]);
            $data = Tools::trim($data);
            foreach($data as $item)
            {
                if(!WarehouseStock::isEnough($warehouse, $item['sku'], $item['qty']))
                {
                    Db::rollback();
                    return sprintf("仓库【%s】SKU【%s】库存不足", $warehouse->name, $item['sku']);
                }
                $stock = WarehouseStock::getWarehouseBySku($warehouse, $item['sku']);
                WarehouseTerpInfo::create([
                    'id_type' => WarehouseBorrow::getTable(),
                    'ref_id' => $borrow->borrow_id,
                    'warehouse_id' => $stock->warehouse_id,
                    'store_id' => $stock->store_id,
                    'price' => $stock->price,
                    'sku' => $item['sku'],
                    'qty' => $item['qty'],
                    'image_url' => $stock->image_url,
                    'remark' => !empty($item['remark']) ? $item['remark'] : '',
                ]);
            }
            Db::commit();
        }catch(DbException $exception)
        {
            Log::info(sprintf("仓库【%s】借用失败，错误信息【%s】", $warehouse->name, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 借用占用库存
     * @param int|string 借用id,或者借用单号
     * @return bool|string
     * @date 2021/01/08
     * @author longli
     */
    public static function borrowLock($borrowSn)
    {
        $borrow = WarehouseBorrow::getBySn($borrowSn);
        if(empty($borrow)) return "借用单【{$borrowSn}】不存在";
        if(!JobFlowModule::isCheckFinish($borrow))
            return  "借用单【{$borrow->borrow_sn}】未审批通过，不能占用库存";
        $stockService = StockService::builder($borrow->borrow_sn, WarehouseBorrow::getTable(), BaseStatus::REF_TYPE_WMS_OUT);
        $warehouse = Warehouse::get($borrow->out_id);
        Db::startTrans();
        foreach($borrow->info as $info)
        {
            $stock = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
            if(($lockMsg = $stockService->lockStock($stock, $info->qty)) !== true)
            {
                Db::rollback();
                return $lockMsg;
            }
        }
        Db::commit();
        return true;
    }

    /**
     * 借用出库
     * @param int|string 借用id,或者借用单号
     * @return bool|string
     * @date 2021/01/04
     * @author longli
     */
    public static function borrowOut($borrowSn)
    {
        $borrow = WarehouseBorrow::getBySn($borrowSn);
        if(empty($borrow)) return "借用单【{$borrowSn}】不存在";
        if(!JobFlowModule::isCheckFinish($borrow))
            return  "借用单【{$borrow->borrow_sn}】未审批通过，不能出库";
        $idType = WarehouseBorrow::getTable();
        $stockService = StockService::builder($borrow->borrow_sn, $idType, BaseStatus::REF_TYPE_WMS_OUT);
        $warehouse = Warehouse::getObj($borrow->out_id);
        try
        {
            Db::startTrans();
            foreach($borrow->info as $info)
            {
                $qty = abs($info->qty);
                $stockOut = WarehouseStock::getWarehouseBySku($warehouse, $info->sku);
                // 释放占用库存
                if(($valiMsg = $stockService->unlockStock($stockOut, $qty, false)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                // 处理出库
                self::stockInfoLog($warehouse, $info->sku, -$qty, $info->price, [
                    'ref_type' => BaseStatus::REF_TYPE_WMS_OUT,
                    'ref_id_type' => $idType,
                    'ref_sn' => $borrow->borrow_sn,
                    'user_id' => $borrow->user_id,
                    'remark' => '借用出库',
                ]);
                self::updateStock($stockOut, -$qty);
            }
            $borrow->status = WarehouseBorrow::STATUS_OUT;
            $borrow->out_date = Tools::now();
            $borrow->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("借用单出库失败【%s】，错误信息【%s】", $borrow->borrow_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 借用退还入库, 支持不同仓退还
     * @param int|string 借用id,或者借用单号
     * @param int $inId 退还仓库id, 默认为出库仓库id
     * @return bool|string
     * @date 2021/01/04
     * @author longli
     */
    public static function borrowReturn($borrowSn, $inId = 0)
    {
        $borrow = WarehouseBorrow::getBySn($borrowSn);
        if(empty($borrow)) return "借用单【{$borrowSn}】不存在";
        if(!in_array($borrow->getData('status'), [WarehouseBorrow::STATUS_WAIT_UP]))
            return sprintf("借用单【%s】状态为【%s】不能入库", $borrow->borrow_sn, $borrow->status);
        $wIn = Warehouse::getObj($inId > 0 ? $inId : $borrow->out_id);
        if(empty($wIn)) return "退还仓库不存在";
        $idType = WarehouseBorrow::getTable();
        try
        {
            Db::startTrans();
            foreach($borrow->info as $info)
            {
                // 已入库不需要操作
                if($info->in_qty >= $info->qty) continue;
                // 处理入库
                if(($valiMsg = self::validateWarehouseStock($wIn, $info->sku, $info->in_qty)) !== true)
                {
                    Db::rollback();
                    return $valiMsg;
                }
                self::stockInfoLog($wIn, $info->sku, $info->in_qty, $info->price, [
                    'ref_type' => BaseStatus::REF_TYPE_WMS_IN,
                    'ref_id_type' => $idType,
                    'ref_sn' => $borrow->borrow_sn,
                    'user_id' => $borrow->user_id,
                    'remark' => '退还入库',
                ]);
                $stockIn = WarehouseStock::getWarehouseBySku($wIn, $info->sku);
                self::updateStock($stockIn, $info->in_qty);
            }
            // 是否全部退还入库
            $borrow->status = WarehouseBorrow::isReturnAll($borrow->borrow_id)
                            ? WarehouseBorrow::STATUS_RETURN_ALL
                            : WarehouseBorrow::STATUS_RETURN_PART;
            $borrow->in_id = $wIn->warehouse_id;
            $borrow->save();
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("借用单【%s】退还入库失败，错误信息【%s】", $borrow->borrow_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 报损出库
     * @param int|WarehouseStock $stock 库存id，或者库存
     * @param int $qty 损坏数量
     * @return bool|string
     * @date 2021/01/04
     * @author longli
     */
    public static function damageOut($stock, $qty)
    {
        $stock = WarehouseStock::getObj($stock);
        if(empty($stock)) return "库存信息不存在";
        $warehouse = Warehouse::getObj($stock->warehouse_id);
        $qty = abs($qty);
        if(!WarehouseStock::isEnough($warehouse, $stock->sku, $qty)) return "SKU【{$stock->sku}】库存不足";
        if(($valiMsg = self::validateWarehouseStock($warehouse, $stock->sku, $qty)) !== true) return $valiMsg;
        try
        {
            Db::startTrans();
            // 记录日志
            self::stockInfoLog($warehouse, $stock->sku, -$qty, $stock->price, [
                'ref_type' => BaseStatus::REF_TYPE_WMS_OUT,
                'ref_id_type' => WarehouseStock::getTable(),
                'ref_sn' => $stock->stock_id,
                'remark' => '报损出库',
            ]);
            // 扣减库存
            self::updateStock($stock, -$qty);
            Db::commit();
        }catch(DbException $exception)
        {
            Log::info(sprintf("仓库【%s】 SKU【%s】报损出库失败，数量【%d】，错误信息【%s】", $warehouse->name, $stock->sku, $qty, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * 盘点库存
     * @param int|string|WarehouseCheck $check 盘点单
     * @param string $sku SKU
     * @param int $qty 盘点库存，现有的数量
     * @param string $remark 盘点备注
     * @return bool|string
     * @date 2021/01/04
     * @author longli
     */
    public static function  checkStock($check, $sku, $qty, $remark = '')
    {
        if($qty < 0) return "SKU【{$sku}】数量不能小于0";
        if(!($check instanceof WarehouseCheck)) $check = WarehouseCheck::getBySn($check);
        if(empty($check)) return "盘点单不存在";
        if(!JobFlowModule::isCheckFinish($check)) return "盘点单【{$check->check_sn}】未审批通过，不能盘点";
        if($check->getData('status') == WarehouseCheck::IS_YES && !empty($check->finish_date))
            return "盘点单【{$check->check_sn}】已结束";
        $stock = WarehouseStock::getWarehouseBySku($check->warehouse, $sku);
        $qty -= $stock->stock;
        if($qty == 0) return true;
        try
        {
            Db::startTrans();
            // 添加盘点详情
            WarehouseTerpInfo::create([
                'id_type' => WarehouseCheck::getTable(),
                'ref_id' => $check->check_id,
                'warehouse_id' => $stock->warehouse_id,
                'store_id' => $stock->store_id,
                'price' => $stock->price,
                'sku' => $stock->sku,
                'image_url' => $stock->image_url,
                'qty' => $qty > 0 ? 0 : abs($qty),
                'in_qty' => $qty > 0 ? $qty : 0,
                'remark' => trim($remark),
            ]);
            // 记录日志
            self::stockInfoLog($check->warehouse, $sku, $qty, $stock->price, [
                'ref_type' => BaseStatus::REF_TYPE_WMS_CHECK,
                'ref_id_type' => WarehouseCheck::getTable(),
                'ref_sn' => $check->check_sn,
                'remark' => '库存盘' . ($qty > 0 ? '盈' : '亏'),
            ]);
            // 更新库存
            self::updateStock($stock, $qty);
            Db::commit();
        }catch (DbException $exception)
        {
            Log::info(sprintf("盘点单【%s】异常，错误信息【%s】", $check->check_sn, $exception->getMessage()));
            Db::rollback();
            return false;
        }
        return true;
    }

    /**
     * @return string
     */
    public function getRefIdType()
    {
        return $this->refIdType;
    }

    /**
     * @return string
     */
    public function getRefSn()
    {
        return $this->refSn;
    }

    /**
     * @return string
     */
    public function getRefType()
    {
        return $this->refType;
    }
}